package fractal.view;

import fractal.action.Fractal1Action;
import fractal.app.AppDefine;
import fractal.model.ColorPalette;
import fractal.model.MandelbrotThread;
import fractal.model.TimeTest;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.MemoryImageSource;
import java.text.NumberFormat;
import javax.swing.JPanel;

/** Class make fractal 1 view */
public class Fractal1Panel extends JPanel implements MouseListener, MouseMotionListener {

	private ColorPalette palette;
	private TimeTest timeTest;
	private int[] buf;
	private int width;
	private int height;
	private double x1;
	private double x2;
	private double y1;
	private double y2;
	private int rep;
	private int dRep;
	private int mark;
	private int markX1;
	private int markX2;
	private int markY1;
	private int markY2;
	private boolean hasBuild;

	/** constructor */
	public Fractal1Panel() {
		this.timeTest = new TimeTest();
		this.palette = new ColorPalette(255, 100, 50);

		this.initValue();
		//this.initView();

		super.addMouseListener(this);
		super.addMouseMotionListener(this);
	}

	private void initValue() {
		this.x1 = -2.8;
		this.x2 = 1.8;
		this.y1 = -2.0;
		this.y2 = 2.0;
		this.dRep = 100;
		this.rep = dRep;
		this.mark = 0;
		this.hasBuild = true;
	}

	/** method initiation view fractal */
	private void initView() {
		JPanel panel = new JPanel(new FlowLayout(2));
		panel.add(new ClickButton("Repaint", new Fractal1Action(AppDefine.FREPAINT, this)));
		panel.add(new ClickButton("Zoom in", new Fractal1Action(AppDefine.FZOOMIN, this)));
		panel.add(new ClickButton("Zoom out", new Fractal1Action(AppDefine.FZOOMOUT, this)));

		super.setLayout(new BorderLayout());
		super.add(panel, BorderLayout.PAGE_END);
	}

	@Override
	/** method print fractal image */
	public void paintComponent(Graphics g) {
		this.timeTest.start();
		this.width = super.getWidth();
		this.height = super.getHeight();

		this.build();
		this.drawImage(g);
		this.drawInfo(g);

		this.timeTest.stop();
		System.out.println("Time: " + this.timeTest.getString());
	}

	private void build() {
		if (this.hasBuild) {
			MandelbrotThread fractal = new MandelbrotThread(this.palette, this.width, this.height);
			fractal.setSeting(this.x1, this.x2, this.y1, this.y2, this.rep);
			fractal.start();

			this.buf = fractal.getArray();
			this.hasBuild = false;
		}
	}

	private void drawImage(Graphics g) {
		MemoryImageSource mis = new MemoryImageSource(this.width, this.height, this.buf, 0, this.width);
		g.drawImage(super.createImage(mis), 0, 0, null, null);
	}

	private void drawInfo(Graphics g) {
		Font font = new Font(Font.DIALOG, Font.PLAIN, 12);
		NumberFormat nf = NumberFormat.getInstance();
		nf.setMaximumFractionDigits(4);
		nf.setMinimumFractionDigits(4);

		g.setFont(font);
		g.drawString("rep = " + this.rep, 10, 20);
		g.drawString("x   = " + nf.format(this.getX(this.markX1)), 10, 35);
		g.drawString("y   = " + nf.format(this.getY(this.markY1)), 10, 50);

		if (mark == 1) {
			g.drawString("x2 = " + nf.format(this.getX(this.markX2)), 10, 65);
			g.drawString("y2 = " + nf.format(this.getY(this.markY2)), 10, 80);

			g.drawLine(this.markX1, this.markY1, this.markX2, this.markY1);
			g.drawLine(this.markX2, this.markY1, this.markX2, this.markY2);
			g.drawLine(this.markX1, this.markY2, this.markX2, this.markY2);
			g.drawLine(this.markX1, this.markY1, this.markX1, this.markY2);
		}
	}

	private void refresh() {
		this.hasBuild = true;
		this.repaint();
	}

	private double getX(int x) {
		return (double) x / (double) width * (x2 - x1) + x1;
	}

	private double getY(int y) {
		return (double) y / (double) height * (y2 - y1) + y1;
	}

	@Override
	public void mousePressed(MouseEvent e) {
		if (this.mark == 0) {
			this.markX1 = e.getX();
			this.markY1 = e.getY();
			this.mark = 1;
		}
	}

	@Override
	public void mouseReleased(MouseEvent e) {
		if (this.mark == 1) {
			this.markX2 = e.getX();
			this.markY2 = e.getY();

			if (this.markX2 < this.markX1) {
				this.markX2 = this.markX1;
				this.markX1 = e.getX();
			}

			if (this.markY2 < this.markY1) {
				this.markY2 = this.markY1;
				this.markY1 = e.getY();
			}

			if ((Math.abs(this.markX2 - this.markX1) > 1) && (Math.abs(this.markY2 - this.markY1) > 1)) {
				this.x1 = getX(this.markX1);
				this.x2 = getX(this.markX2);
				this.y1 = getY(this.markY1);
				this.y2 = getY(this.markY2);
				this.rep += this.dRep;
				this.refresh();
			}
			this.mark = 0;
		}
	}

	@Override
	public void mouseMoved(MouseEvent e) {
		this.markX1 = e.getX();
		this.markY1 = e.getY();
		repaint();
	}

	@Override
	public void mouseDragged(MouseEvent e) {
		if (this.mark == 1) {
			this.markX2 = e.getX();
			this.markY2 = e.getY();
			repaint();
		}
	}

	@Override
	public void mouseClicked(MouseEvent e) {
	}

	@Override
	public void mouseEntered(MouseEvent e) {
	}

	@Override
	public void mouseExited(MouseEvent e) {
	}
}
